﻿/*****************************************介绍*****************************************
 * 作者：User
 * 创建时间：2025-09-03 13:39:47
 * 功能：资源缓存服务
 *      1、提供视频缓存服务
 *      2、提供图片缓存服务
**************************************************************************************/

using Game.Framework;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;

[CustomModule(13)]
public class AssetCacheServer : SingletonMonoAuto<AssetCacheServer>, IModule
{
    private InitResult m_InitResult;
    private Dictionary<string, VideoCacheInfo> m_VideoCache = new Dictionary<string, VideoCacheInfo>();
    private Dictionary<string, Sprite> m_SpriteCache = new Dictionary<string, Sprite>();
    private string m_VideoCachePath;
    private string m_SpriteCachePath;
    private string m_DefaultSpritePath;
    private Sprite m_DefaultSprite;

    void IModule.Init()
    {
        m_InitResult = InitResult.Wait;
        m_VideoCachePath = Path.Combine(Application.persistentDataPath, "VideoCache");
        m_SpriteCachePath = Path.Combine(Application.persistentDataPath, "ImageCache");
        m_DefaultSpritePath = Path.Combine(Application.streamingAssetsPath, "Default.jpg");

        // 加载本地缓存的资源
        StartCoroutine(LoadCache());
    }

    private IEnumerator LoadCache()
    {
        // 视频
        if (!Directory.Exists(m_VideoCachePath)) Directory.CreateDirectory(m_VideoCachePath);
        foreach (string file in Directory.GetFiles(m_VideoCachePath))
        {
            string fileName = Path.GetFileNameWithoutExtension(file);
            VideoCacheInfo info = new VideoCacheInfo();
            string[] strs = fileName.Split('_');
            if (strs.Length != 3) continue;
            info.url = file;
            if (!int.TryParse(strs[2], out info.id)) continue;
            m_VideoCache.TryAdd(fileName, info);
            LogManager.Log($"key:{fileName} value:{file}", "AssetCacheServer", "LoadCache", "Video");
            yield return null;
        }

        // 图片
        if (!Directory.Exists(m_SpriteCachePath)) Directory.CreateDirectory(m_SpriteCachePath);
        foreach (string file in Directory.GetFiles(m_SpriteCachePath))
        {
            string fileName = Path.GetFileNameWithoutExtension(file);
            byte[] data = File.ReadAllBytes(file);
            Texture2D texture = new Texture2D(2, 2);
            texture.LoadImage(data);
            Sprite sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f), 100f);
            m_SpriteCache.TryAdd(fileName, sprite);
            LogManager.Log($"key:{fileName} width:{sprite.texture.width} hight:{sprite.texture.height} path:{file}", "AssetCacheServer", "LoadCache", "Sprite");
            yield return null;
        }

        // 默认图片
        byte[] defBytes = File.ReadAllBytes(m_DefaultSpritePath);
        Texture2D defTexture = new Texture2D(2, 2);
        defTexture.LoadImage(defBytes);
        m_DefaultSprite = Sprite.Create(defTexture, new Rect(0, 0, defTexture.width, defTexture.height), new Vector2(0.5f, 0.5f), 100f);

        m_InitResult.type = E_ResultType.Success;
        m_InitResult.error = string.Empty;
        m_InitResult.progress = 1.0f;
    }

    public VideoCacheInfo GetVideoInfo(EAssetType type, int id)
    {
        string key = GetAssetKey(type, id, true);
        m_VideoCache.TryGetValue(key, out VideoCacheInfo result);
        return result;
    }

    public Sprite GetSprite(EAssetType type, int id, bool isRaw = false)
    {
        string key = GetAssetKey(type, id, isRaw);
        m_SpriteCache.TryGetValue(key, out Sprite result);
        return result;
    }

    /// <summary>缓存视频到本地</summary>
    /// <param name="videoUrl">视频原始地址（使用该地址请求真正的播放地址）</param>
    /// <param name="reqUrl">请求地址（真正可播放的地址）</param>
    /// <param name="id">视频id</param>
    /// <param name="progress">进度回调，第一个参数：已下载大小，第二个参数：总大小，单位：byte</param>
    public void CacheVideo(string reqUrl, int id)
    {
        if (string.IsNullOrWhiteSpace(reqUrl))
        {
            LogManager.LogError($"url 不能为空！", "AssetCacheServer", "CacheVideo");
            return;
        }
        StartCoroutine(CheckVideo_Internal(reqUrl, id));
    }

    private IEnumerator CheckVideo_Internal(string reqUrl, int id)
    {
        var key = GetAssetKey(EAssetType.Video, id, true);

        if (m_VideoCache.TryGetValue(key, out VideoCacheInfo info))
        {
            if (info != null)
                EventCenter.Instance.Trigger(GameEventName.CACHE_VIDEO_COMPLATE, m_VideoCache[key]);
            else
                LogManager.Log($"key:{key} caching...", "AssetCacheServer", "CacheVideo");
            yield break;
        }
        var savePath = Path.Combine(m_VideoCachePath, $"{key}.mp4");
        m_VideoCache.Add(key, null);

        // 获取视频大小
        var headReq = UnityWebRequest.Head(reqUrl);
        headReq.SendWebRequest();
        while (!headReq.isDone) { yield return new WaitForSeconds(0.01f); }

        if (headReq.result != UnityWebRequest.Result.Success) yield break;

        string totalSizeStr = headReq.GetResponseHeader("Content-Length");
        long total = long.Parse(totalSizeStr);
        LogManager.Log($"视频大小：{total / Mathf.Pow(1024, 2):f2} MB", "AssetCacheServer", "CacheVideo");

        // 缓存视频
        yield return StartCoroutine(CacheVideo_Internal(reqUrl, key, savePath, total, id));
    }

    private IEnumerator CacheVideo_Internal(string reqUrl, string key, string savePath, long totalSize, int id)
    {
        using (var req = UnityWebRequest.Get(reqUrl))
        {
            req.downloadHandler = new DownloadHandlerBuffer();
            req.SendWebRequest();
            while (!req.isDone)
            {
                EventCenter.Instance.Trigger(GameEventName.CACHE_VIDEO_PROGRESS, id, (long)req.downloadedBytes, totalSize);
                LogManager.Log($"缓存视频中，id:{id} key:{key} [{req.downloadedBytes / Mathf.Pow(1024, 2):f2}/{totalSize / Mathf.Pow(1024, 2):f2}]", "AssetCacheServer", "CacheVideo");
                yield return null;
            }

            if (req.result == UnityWebRequest.Result.Success)
            {
                m_VideoCache[key] = new VideoCacheInfo() { id = id, url = savePath };
                File.WriteAllBytes(savePath, req.downloadHandler.data);
                LogManager.Log($"缓存视频完成，id:{id} key:{key}", "AssetCacheServer", "CacheVideo");
                EventCenter.Instance.Trigger(GameEventName.CACHE_VIDEO_PROGRESS, id, totalSize, totalSize);
                EventCenter.Instance.Trigger(GameEventName.CACHE_VIDEO_COMPLATE, m_VideoCache[key]);
            }
            else
            {
                LogManager.LogError($"缓存视频失败，{req.error}，重新缓存", "AssetCacheServer", "CheckVideo");
                yield return new WaitForSeconds(5f);
                StartCoroutine(CacheVideo_Internal(reqUrl, key, savePath, totalSize, id));
            }
        }
    }

    // 缓存图片到本地
    public void CacheSprite(EAssetType type, int id, string reqUrl, bool isRaw = false, Action<Sprite> complate = null)
    {
        if (string.IsNullOrWhiteSpace(reqUrl))
        {
            LogManager.LogError($"url 不能为空！", "AssetCacheServer", "CacheSprite");
            complate?.Invoke(null);
            return;
        }
        string key = GetAssetKey(type, id, isRaw);
        if (m_SpriteCache.TryGetValue(key, out Sprite sprite))
        {
            if (sprite != null)
                complate?.Invoke(sprite);
            else
                LogManager.Log($"key:{key} caching...", "AssetCacheServer", "CacheSprite");
            return;
        }
        m_SpriteCache.Add(key, null);
        CommonMono.Instance.GetSpriteAsync(reqUrl, sprite =>
        {
            if (sprite == null)
            {
                m_SpriteCache.Remove(key);
                complate?.Invoke(null);
                return;
            }
            // 保存到本地
            m_SpriteCache[key] = sprite;
            bool hasAlpha = HasAlphaChannel(sprite.texture.format);
            string path = Path.Combine(m_SpriteCachePath, $"{key}.{(hasAlpha ? "png" : "jpg")}");
            byte[] data = hasAlpha ? sprite.texture.EncodeToPNG() : sprite.texture.EncodeToJPG();
            File.WriteAllBytes(path, data);
            LogManager.Log($"key:{key} width:{sprite.texture.width} hight:{sprite.texture.height} path:{path}", "AssetCacheServer", "CacheSprite");
            complate?.Invoke(sprite);
        }, 6);
    }

    public Sprite GetDefaultBg() => m_DefaultSprite;

    public string GetAssetPath(EAssetType type, int id, bool isRaw = true)
    {
        string key = GetAssetKey(type, id, isRaw);
        switch (type)
        {
            case EAssetType.Img:
                if (File.Exists(Path.Combine(m_SpriteCachePath, $"{key}.png")))
                    return Path.Combine(m_SpriteCachePath, $"{key}.png");
                else if (File.Exists(Path.Combine(m_SpriteCachePath, $"{key}.jpg")))
                    return Path.Combine(m_SpriteCachePath, $"{key}.jpg");
                else
                    return "";
            case EAssetType.Video:
                return Path.Combine(m_VideoCachePath, $"{key}.mp4");
            case EAssetType.Gif:
            default:
                return "";
        }
    }

    public string DefaultBgPath => m_DefaultSpritePath;

    private bool HasAlphaChannel(TextureFormat format)
    {
        switch (format)
        {
            case TextureFormat.ARGB32:
            case TextureFormat.RGBA32:
            case TextureFormat.RGBA4444:
            case TextureFormat.BGRA32:
            case TextureFormat.DXT5:        // 有alpha
            case TextureFormat.PVRTC_RGBA2:
            case TextureFormat.PVRTC_RGBA4:
            case TextureFormat.ETC2_RGBA8:
                return true;
            default:
                return false;
        }
    }

    InitResult IModule.GetInitResult()
    {
        return m_InitResult;
    }

    void IModule.Clear()
    {
        m_VideoCache.Clear();
        m_SpriteCache.Clear();
    }

    /// <summary>获取资源的Key，统一接口</summary>
    public string GetAssetKey(EAssetType type, int id, bool isRaw) => $"{type}_{(isRaw ? "r" : "s")}_{id}";

    public IEnumerable<string> AllSpriteKeys => m_SpriteCache.Keys;
    public IEnumerable<string> AllVideoKeys => m_VideoCache.Keys;
}

public class VideoCacheInfo
{
    public int id;
    public string url;
}

// 资源类型
public enum EAssetType
{
    Img,        // 图片
    Gif,        // 动图
    Video,      // 视频
}